package com.hk.commons.poi.excel.read.validate;

import com.hk.commons.poi.excel.exception.ExcelReadException;
import com.hk.commons.poi.excel.metadata.InvalidCell;
import com.hk.commons.poi.excel.metadata.Title;
import com.hk.commons.util.CollectionUtils;
import com.hk.commons.util.Lazy;
import com.hk.commons.util.SpringContextHolder;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.ValidatorFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.ApplicationContext;
import org.springframework.lang.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 使用 JSR-303 注解验证
 *
 * @author kevin
 * @date 2018年1月10日下午5:24:31
 */
public class JSRRowValidate<T> implements RowValidate<T> {

    /**
     * @see org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration#defaultValidator(ApplicationContext, ObjectProvider)
     */
    private static final Lazy<ValidatorFactory> VALIDATOR_FACTORY = Lazy.of(() -> SpringContextHolder.getBeanIfExist(ValidatorFactory.class)
            .orElseGet(() -> Validation.byDefaultProvider().configure().buildValidatorFactory()));

    private final Class<?>[] groups;

    /**
     * @param groups groups
     */
    public JSRRowValidate(Class<?>... groups) {
        this.groups = (null == groups) ? new Class<?>[]{} : groups;
    }

    /**
     * 验证对象并将不通过的单元格转换为对象
     *
     * @param t          验证对象
     * @param rowNumber  对象所在Excel 行
     * @param titleList  标题行
     * @param sheetIndex sheetIndex
     * @param sheetName  sheetName
     * @return 不通过的单元格转换为对象
     */
    @Override
    public List<InvalidCell> validate(T t, int rowNumber, List<Title> titleList, int sheetIndex, String sheetName) {
        return getValidateInfo(VALIDATOR_FACTORY.get().getValidator().validate(t, groups), rowNumber, titleList);
    }

    /**
     * 将验证失效的属性转换为失效的单元格
     *
     * @param violationSet 对象的错误信息
     * @param rowNumber    对象所在Excel 行
     * @param titleList    对应的标题集合
     * @return 验证失效的单元格
     */
    @Nullable
    private List<InvalidCell> getValidateInfo(Set<ConstraintViolation<Object>> violationSet, int rowNumber,
                                              List<Title> titleList) {
        if (CollectionUtils.isNotEmpty(violationSet)) {
            var result = new ArrayList<InvalidCell>(violationSet.size());
            var propertyNameMap = titleList.stream()
                    .collect(Collectors.toMap(Title::getPropertyName, Function.identity()));
            violationSet.forEach(item -> {
                var propertyName = item.getPropertyPath().toString();
                var findTitle = propertyNameMap.get(propertyName);
                if (Objects.isNull(findTitle)) {
                    throw new ExcelReadException(STR."根据属性名[\{propertyName}]找不到对应的标题");
                }
                result.add(new InvalidCell(rowNumber, findTitle.getColumn(), item.getInvalidValue(), findTitle,
                        item.getMessageTemplate()));
            });
            return result;
        }
        return null;
    }
}
